package belf.migrate.engine.ddl;

import belf.migrate.api.model.QualifiersModel;
import belf.migrate.api.table.schema.Catalog;
import belf.migrate.core.exception.BelfRuntimeException;
import belf.migrate.core.jdbc.DBFunctionMapping;
import belf.migrate.core.jdbc.DBTypeMapping;
import belf.migrate.core.jdbc.FunctionModel;
import belf.migrate.core.jdbc.TypeModel;
import org.antlr.v4.runtime.tree.ParseTree;

import java.util.*;
import java.util.stream.Collectors;

public class BaseDDLConvert implements DDLConvert {
    // 常用的SQL关键词，目前还不全，还需要后续继续补充。转化后的DDL中，关键词前后都需要加空格
    public final static List<String> SQL_KEYWORDS = Arrays.asList(
            "SELECT",
            "IS",
            "AS",
            "FROM",
            "WHERE",
            "LIKE",
            "NOT",
            "NULL",
            "ORDER BY",
            "LEFT",
            "RIGHT",
            "JOIN",
            "ON",
            "GROUP BY",
            "LIMIT",
            "HAVING",
            "SET",
            "ROW",
            "ROWS",
            "ONLY",
            "WITH",
            "DISTINCT",
            "UNIQUE",
            "TOP",
            "CREATE",
            "ALTER",
            "DELETE",
            "INSERT",
            "DELETE",
            "BEGIN",
            "END");
    /** 对象名称，如表名、视图名、Class名、Type名、触发器名、存储过程名 */
    protected String objectName;
    /** 创建对象的DDL语句 */
    protected String sql;
    /** 源数据库的catalog名称，取值参考{@link belf.migrate.api.common.DatabaseIdentifier} */
    protected Catalog sourceCatalog;
    /** 目标数据库的catalog名称，取值参考{@link belf.migrate.api.common.DatabaseIdentifier} */
    protected Catalog sinkCatalog;
    protected String schema;    //如果指定了schema，则生成的VIEW DDL中，所有的表都需要加上前缀schema名
    /** 标识符左侧的引号格式 */
    protected String leftQuote;
    /** 标识符右侧的引号格式 */
    protected String rightQote;

    protected Map<String, FunctionModel> funcMapping;
    protected Map<String, TypeModel> typeMapping;

    protected QualifiersModel qualifiersModel;

    protected String taskType;

    protected List<BelfRuntimeException> warnings = new ArrayList<>();

    /**
     * 带schema的构造函数。
     * @param objectName 对象名称，如表名、视图名、Class名、Type名、触发器名、存储过程名
     * @param sql 对象对应的DDL创建语句
     * @param sourceCatalog 源库Catalog
     * @param sinkCatalog 目标库Catalog
     * @param schema 指定了schema，则生成的VIEW DDL中，所有的表都需要加上前缀schema名
     */
    public BaseDDLConvert(String objectName, String sql, Catalog sourceCatalog, Catalog sinkCatalog,
                          String schema, QualifiersModel qualifiersModel) {
        this.objectName = objectName;
        this.sql = sql;
        this.sourceCatalog = sourceCatalog;
        this.sinkCatalog = sinkCatalog;
        this.schema = schema;
        this.leftQuote = qualifiersModel.getLeftQuote();
        this.rightQote = qualifiersModel.getRightQuote();
        this.qualifiersModel = qualifiersModel;

        this.taskType = sourceCatalog.name() + "-" + sinkCatalog.name();
        this.funcMapping = DBFunctionMapping.getInstance().getMapping(taskType);
        this.typeMapping = DBTypeMapping.getInstance().getMapping(taskType);

    }

    @Override
    public String convert() {
        return sql;
    }

    public String getObjectName() {
        return objectName;
    }

    public String getSql() {
        return sql;
    }

    public Catalog getSourceCatalog() {
        return sourceCatalog;
    }

    public Catalog getSinkCatalog() {
        return sinkCatalog;
    }

    public String getLeftQuote() {
        return leftQuote;
    }

    public String getRightQote() {
        return rightQote;
    }

    public QualifiersModel getQualifiersModel() {
        return qualifiersModel;
    }

    public Map<String, FunctionModel> getFuncMapping() {
        return funcMapping;
    }

    public Map<String, TypeModel> getTypeMapping() {
        return typeMapping;
    }

    public void addWarning(BelfRuntimeException exception) {
        Map<String, BelfRuntimeException> map = warnings.stream()
                .collect(Collectors.toMap(
                        e -> e.geteErrorCode() + e.getMessage(),
                        person -> person
                ));
        if (!map.containsKey(exception.geteErrorCode() + exception.getMessage())) {
            warnings.add(exception);
        }
    }

    public List<BelfRuntimeException> getWarnings() {
        return warnings;
    }

    public String getSchema() {
        return schema;
    }

    /**
     * 节点node是否有指定类名的父节点
     * @param ancestorClassName 父节点类名
     * @param node
     * @return
     */
    public static boolean hasAncestor(String ancestorClassName, ParseTree node) {
        ParseTree parent = node.getParent();
        while (parent != null) {
            if (parent.getClass().getName().endsWith(ancestorClassName)) {
                return true;
            }
            parent = parent.getParent();
        }
        return false;
    }

    /**
     * 找到指定类名的父节点
     * @param ancestorClassName
     * @param node
     * @return 返回指定类名的父节点；如果不存在，返回null
     */
    public static ParseTree getParent(String ancestorClassName, ParseTree node) {
        ParseTree parent = node.getParent();
        while (parent != null) {
            if (parent.getClass().getName().endsWith(ancestorClassName)) {
                return parent;
            }
            parent = parent.getParent();
        }
        return null;
    }

    public String getTaskType() {
        return taskType;
    }
}
